﻿using System;
using System.Diagnostics.CodeAnalysis;
using UnityEngine;

public class Joystick : MonoBehaviour
{
    public enum JoystickState
    {
        Idle,
        Up,
        Down,
        Move,
    }

    // Input.mousePosition 屏幕左下角00点, 正方向上
    // 所有的位置都是相对屏幕左下角的位置
    
    private Transform trans;
    private Transform joystickTransform;
    private Transform stickTransform;
    private Transform directionTransform;
    
    public bool enable = true;

    public bool fixedUIPosition;

    public bool clickPosAsOrgin;
    
    [NonSerialized]
    public JoystickState state = JoystickState.Idle;
    
    public float maxRange = 50;
    
    /// 按下状态转移到拖动状态的最小移动距离
    public float minMoveDis2 = 4;

    private Vector3 initOrigin = Vector3.zero;
    
    /// 原点位置 
    [NonSerialized]
    public Vector3 orignPos = Vector3.zero;
    
    /// 记录的位置
    [NonSerialized]
    public Vector3 currentMousePos = Vector3.zero;

    private Vector3 direction = Vector3.zero;
    private float magnitude = 0;
    
    /// 实际的位置
    [NonSerialized]
    public Vector3 realMousePos = Vector3.zero;

    private bool handleByMouse;
    
    /// 按下状态时, 鼠标实际位置和记录位置的距离的平方
    private float DownOffset2 => Mathf.Pow(realMousePos.x - currentMousePos.x, 2) + Mathf.Pow(realMousePos.y - currentMousePos.y, 2);

    /// 鼠标实际位置到原点
    private Vector3 Direction => realMousePos - orignPos;

    private bool RealPosInRange => Direction.magnitude <= maxRange;

    [NonSerialized]
    public Action<Vector3, float> EventDown;
    [NonSerialized]
    public Action EventUp;
    [NonSerialized]
    public Action<Vector3, float> EventMove;

    public static Joystick instance;
    private void Awake()
    {
        instance = this;
        
        trans = transform;
        joystickTransform = trans.Find("joystick");
        stickTransform = trans.Find("joystick/stick");
        directionTransform = trans.Find("joystick/direction");

        initOrigin = orignPos = currentMousePos = trans.position;

        // Joystick.instance.EventDown = () => { UnityEngine.Debug.Log("OnDown"); };
        // Joystick.instance.EventMove = (v, len) => { UnityEngine.Debug.Log($"direction {v}    magnitude {len}"); }; 
        // Joystick.instance.EventUp = () => { UnityEngine.Debug.Log("OnUp"); };
    }

    void Start()
    {
    }

    void Update()
    {
        if (enable)
        {
            realMousePos = Input.mousePosition;

            //按下
            if (Input.GetMouseButtonDown(0))
            {
                if (SwitchState(JoystickState.Down))
                {
                    handleByMouse = true;
                }
            }

            //放开
            if (Input.GetMouseButtonUp(0))
            {
                SwitchState(JoystickState.Up);
            }

            if (!handleByMouse)
            {
                ProcessKey();
            }

            StateUpdate(state);
        }
    }

    public bool SwitchState(JoystickState state)
    {
        if (this.state == state) return false;
        StateExit(this.state);
        this.state = state;
        StateEnter(this.state);
        return true;
    }

    private void StateEnter(JoystickState state)
    {
        switch (state)
        {
            case JoystickState.Idle:
            {
                IdleEnter();
                break;
            }
            
            case JoystickState.Down:
            {
                DownEnter();
                break;
            }
            
            case JoystickState.Move:
            {
                MoveEnter();
                break;
            }
            
            case JoystickState.Up:
            {
                UpEnter();
                break;
            }
        }
    }

    private void StateUpdate(JoystickState state)
    {
        switch (state)
        {
            case JoystickState.Down:
            {
                DownUpdate();
                break;
            }
            
            case JoystickState.Move:
            {
                MoveUpdate();
                break;
            }
        }
    }   

    private void StateExit(JoystickState state)
    {
        switch (state)
        {
            case JoystickState.Move:
            {
                MoveExit();
                break;
            }
            
            case JoystickState.Up:
            {
                UpExit();
                break;
            }
        }
    }
    
    void IdleEnter()
    {
        currentMousePos = orignPos;
        UpdateStickPosition();
        directionTransform.gameObject.SetActive(false);
    }

    private void DownEnter()
    {
        if(clickPosAsOrgin && !RealPosInRange) orignPos = realMousePos;
        SetCurrentMousePosition(realMousePos);
        UpdateUIPosition();
        UpdateStickPosition();
        EventDown?.Invoke(direction, magnitude);
    }

    private void DownUpdate()
    {
        if (handleByMouse && DownOffset2 < minMoveDis2) return;
        SwitchState(JoystickState.Move);
    }
    
    private void MoveEnter()
    {
        // 显示方向
    }

    private void MoveUpdate()
    {
        SetCurrentMousePosition(realMousePos);
        UpdateStickPosition();
        EventMove?.Invoke(direction, magnitude);
        
        // 更新方向
    }
    
    private void MoveExit()
    {
        // 隐藏方向
    }

    private void UpEnter()
    {
        // 暂时无事 直接进入待机
        SwitchState(JoystickState.Idle);
    }

    private void UpExit()
    {
        handleByMouse = false;
        orignPos = initOrigin;
        UpdateUIPosition();
        EventUp?.Invoke();
    }

    private void SetCurrentMousePosition(Vector3 v)
    {
        direction = v - orignPos;
        magnitude = Mathf.Min(direction.magnitude / maxRange, 1);
        direction.Normalize();
        currentMousePos = orignPos + direction * magnitude * maxRange;
    }

    private void UpdateUIPosition()
    {
        if (!fixedUIPosition) joystickTransform.localPosition = orignPos - trans.position;
    }

    private void UpdateStickPosition()
    {
        stickTransform.localPosition = currentMousePos - orignPos;
    }

    private void ProcessKey()
    {
        var d = GetKeyValue(KeyCode.D);
        var a = GetKeyValue(KeyCode.A);
        var w = GetKeyValue(KeyCode.W);
        var s = GetKeyValue(KeyCode.S);

        var v = new Vector3(d - a, w - s);
        v.Normalize();
        
        realMousePos = orignPos + v * maxRange;
        
        if (d != 0 || a != 0 || w != 0 || s != 0)
        {
            if (state == JoystickState.Idle)  SwitchState(JoystickState.Down);
        }
        else
        {
            if (state == JoystickState.Down || state == JoystickState.Move) SwitchState(JoystickState.Up);
        }
    }

    private float GetKeyValue(KeyCode key)
    {
        return Input.GetKeyDown(key) || Input.GetKey(key) ? 1 : 0;
    }

    void OnDestroy()
    {
        EventDown = null;
        EventMove = null;
        EventUp = null;
        instance = null;
    }
}
